﻿using Jint.Native;
using Jint.Runtime.Interop;
using QuickJS;
using QuickJS.Native;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using TTRom;

namespace runtime_standalone
{
    class Vector2JS
    {
        public Vector2JS(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
        public int x;
        public int y;
        public override string ToString()
        {
            return "(" + x + "," + y + ")";
        }
        public Vector2JS Clone()
        {
            return new Vector2JS(x, y);
        }
        public static string ver
        {
            get
            {
                return "0.00";
            }
        }
    }
    internal class Runtime : ITTRuntime
    {
        Jint.Engine jintEngine;
        QuickJS.QuickJSRuntime runtime;
        QuickJS.QuickJSContext context;
        public Runtime()
        {
            jintEngine = new Jint.Engine((op) =>
            {

            });
            runtime = new QuickJS.QuickJSRuntime();
            context = new QuickJS.QuickJSContext(runtime);
        }
        void debug_print(JsValue v)
        {


            if (v.IsString())
            {
                Console.WriteLine("[str]" + v.AsString());
                return;
            }
            if (v != null)
            {
                Console.WriteLine(v.Type + "=" + v.ToString());
            }
            else
            {
                Console.WriteLine("null");
            }
            return;
        }
        Action<JsValue> backfunc;
        void setcallback(Action<JsValue> back)
        {
            backfunc = back;
        }
        void TestJINT()
        {
            jintEngine.SetValue("print", debug_print);
            jintEngine.SetValue("vector2", TypeReference.CreateTypeReference(jintEngine, typeof(Vector2JS)));
            jintEngine.SetValue("setcallback", setcallback);


            jintEngine.Execute("var a=3");
            //call func
            jintEngine.Execute("print(a)");

            jintEngine.Execute("print(JSON.stringify({'abc':3}))");


            //use class
            {
                //注意js 没有对结构体的复制语法，所以干脆不要用结构体，避免误解
                //JSON.stringify 不能用于传入的类型
                var code = @"
                    let v1= new vector2(3,3);
                    let v2= v1.Clone();
                    v1.x=5;
                    print(v2);
                    print(v1);
                    print('v1='+JSON.stringify(v1));
                    print('v1='+v1.ToString());
                    print(vector2.ver);
                    ";
                jintEngine.Execute(code);
            }

            //use js object
            {
                //注意js 没有对结构体的复制语法，所以干脆不要用结构体，避免误解
                //JSON.stringify 不能用于传入的类型
                var code = @"


                    let arr= [1,2,3,4,5];
                    print(arr);
                    let b= false;
                    print(b);
                    let o = {'ab':1,'abb':2};
                    print(o);

                    //let c =new Board();
                    //print(c);
                    ";
                jintEngine.Execute(code);
            }
            //set js func as callback
            {
                //注意js 没有对结构体的复制语法，所以干脆不要用结构体，避免误解
                //JSON.stringify 不能用于传入的类型
                var code = @"
        var cb = function (val) {
                      //print('callback->'+val);
                        var ss ='';
                        for(var i=0;i<1000;i++)
                        {
                        }
                    }
     
                    setcallback(cb);
                    ";
                jintEngine.Execute(code);

                DateTime t0 = DateTime.Now;
                for (var i = 0; i < 1000; i++)
                {
                    backfunc.Invoke(new JsValue(i + 3));
                }
                DateTime t1 = DateTime.Now;
                Console.WriteLine("time=" + (t1 - t0).TotalMilliseconds);
            }
        }
        void TestQuickJS()
        {
            Console.WriteLine("==TestQuickJS");
            context.GetGlobal().DefineFunction("setcallback", callbackquickjs, 1, QuickJS.JSPropertyFlags.CWE);
            context.GetGlobal().DefineFunction("print", printHello, 1, QuickJS.JSPropertyFlags.CWE);
            QuickJSValue ttname = QuickJSValue.Create(context, JSClassID.Object);
            context.GetGlobal().DefineProperty("tt", ttname, JSPropertyFlags.Normal);
            ttname.DefineFunction("print", printHello, 1, QuickJS.JSPropertyFlags.Normal);

            context.Eval("var a=3", "", QuickJS.JSEvalFlags.Global);
            //call func
            context.Eval("tt.print(a)", "", QuickJS.JSEvalFlags.Global);

            context.Eval("print(JSON.stringify({'abc':3}))", "", QuickJS.JSEvalFlags.Global);

            //QuickJS's bind is so awful. so we use func mode for it.

            //set js func as callback
            {
                //注意js 没有对结构体的复制语法，所以干脆不要用结构体，避免误解
                //JSON.stringify 不能用于传入的类型
                var code = @"
        var cb = function (val) {
                      //print('callback->'+val);
                        var ss ='';
                        for(var i=0;i<1000;i++)
                        {
                        }
                    }
                    print('setcall');
                    setcallback(cb);
                    ";
                context.Eval(code, "", QuickJS.JSEvalFlags.Global);

                DateTime t0 = DateTime.Now;
                for (var i = 0; i < 1000; i++)
                {
                    qjsback();
                }
                DateTime t1 = DateTime.Now;
                Console.WriteLine("time=" + (t1 - t0).TotalMilliseconds);
            }

        }
        private static JSValue printHello(JSContext ctx, JSValue thisArg, int argc, JSValue[] argv)
        {
            var v = argv[0];
            if (v == null)
                Console.WriteLine("null");
            else
            {
                Console.WriteLine(v.Tag + "=" + v.ToJSON(ctx));
            }
            return JSValue.Undefined;
        }
        QuickJSValue qjscallback;
        private JSValue callbackquickjs(JSContext ctx, JSValue thisArg, int argc, JSValue[] argv)
        {
            var v = argv[0];
            if (v == null)
                Console.WriteLine("null");
            var b = v.IsFunction(ctx);

            qjscallback = QuickJSValue.Wrap(context, v);

            Console.WriteLine("setcall");
            return JSValue.Undefined;
        }
        void qjsback()
        {
            var r = qjscallback.Call(JSValue.Undefined, JSValue.Create(1));
            //unsafe
            //{
            
            //    var p = new JSValue[] { JSValue.Create(1), JSValue.Null };
            //    fixed (JSValue* ptr = p)
            //    {

            //        QuickJSNativeApi.JS_Call(context.NativeInstance, JSValue.Null, qjscallback, 1, ptr);
            //    }

            //}
        }
        public void TestJS()
        {
            TestJINT();
            TestQuickJS();
        }
        public void LoadFromFolder(string path)
        {
            throw new NotImplementedException();
        }

        public void LoadFromZip(string zipfile)
        {
            throw new NotImplementedException();
        }

        public void Update()
        {
            throw new NotImplementedException();
        }
    }
}
